package org.codetranslators.tools.XMLUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;

import org.codetranslators.common.ControlFlowGraph;
import org.codetranslators.common.ControlFlowNode;
import org.codetranslators.common.Node;
import org.codetranslators.compiler.intermrep.AssemblyInstruction;
import org.codetranslators.compiler.intermrep.CJump;
import org.codetranslators.compiler.intermrep.Call;
import org.codetranslators.compiler.intermrep.Jump;
import org.codetranslators.compiler.intermrep.LabelInstr;
import org.codetranslators.compiler.intermrep.Operation;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;


public class CFGXMLConverter {

	ArrayList<NodeInfoBean> nodesInfo = new ArrayList<NodeInfoBean>();
	//int id = 1;

	public void generatetXML(Vector<ControlFlowGraph> cfgs) {	

		
		Document document = DocumentHelper.createDocument();
		System.out.println(document.getXMLEncoding());
		Element root = document.addElement("cfg-set");
		//StringWriter writer = new StringWriter();
		//XMLWriter xmlWriter = new XMLWriter(writer);
		//xmlWriter.write(document);	

		for(ControlFlowGraph cfg:cfgs){
		
			nodesInfo = new ArrayList<NodeInfoBean>();
			generateDataForXml(cfg);
			
			Element xmlCFG = root.addElement("cfg");		
	
			for(int i = 0; i <nodesInfo.size(); i++){
				if(nodesInfo.get(i).getNode().getName().equals(cfg.getStartNode().getName()))
					addNodesToXml(xmlCFG, nodesInfo.get(i), true, false);
				else if(nodesInfo.get(i).getNode().getName().equals(cfg.getEndNode().getName()))
					addNodesToXml(xmlCFG,nodesInfo.get(i),false,true);
				else 
					addNodesToXml(xmlCFG,nodesInfo.get(i),false,false);
			}
		}



		//document.addDocType("catalog",null,"file://c:/Dtds/catalog.dtd");
		try{
			OutputFormat format = OutputFormat.createPrettyPrint();
			// format.setNewlines(true);
			format.setAttributeQuoteCharacter('\'');
			format.setNewLineAfterDeclaration(true);
			format.setTrimText(true);
			// format.setLineSeparator("");
			System.out.println(format.getEncoding());
			System.out.println(format.getIndent());
			FileWriter writer = new FileWriter(new File("xmlFromCFG.xml"));
			XMLWriter output = new XMLWriter(writer,format);
			output.write( document );
			output.close();
		}
		catch(IOException e){
			System.out.println(e.getMessage());
		}

	}

	public void addNodesToXml(Element element, NodeInfoBean nodeInfoBean, boolean startNode, boolean endNode){

		Element xmlNode = element.addElement("node");		
		Node node = nodeInfoBean.getNode();
		xmlNode.addAttribute("name", node.getName());
		xmlNode.addAttribute("id", nodeInfoBean.getId()+"");
		if(startNode){			
			xmlNode.addAttribute("startNode", "y");			
		}
		else if(endNode){			
			xmlNode.addAttribute("endNode", "y");

		}
		ControlFlowNode controlFlowNode = (ControlFlowNode)node;
		Vector defenitions =  new Vector();
		Vector srcList ;
		//BitSet set = controlFlowNode.getUsesBitVector();
		Vector<AssemblyInstruction> instructions = controlFlowNode.getInstructions();
		for(AssemblyInstruction ins:instructions){
			if(ins.getDestList()!=null && ins.getDestList().size()!=0)
			defenitions  = ins.getDestList();
			srcList = ins.getSrcList();
			Element instruction = xmlNode.addElement("instruction");
			instruction.setAttributeValue("ins", ins.getAssemCode(true));
			instruction.addAttribute("type", getInstructionType(ins.getType(),ins));
			//instruction.addAttribute("iv",ins. )
			if(ins.getType()==AssemblyInstruction.CALL){
				Call call = (Call)ins;
				instruction.addAttribute("fname",call.getFunctionName());
			}
			if(ins.getType()==AssemblyInstruction.CJUMP){
				//instruction.addAttribute("", arg1)
				CJump  cjump = (CJump)ins;
				instruction.addAttribute("ifLabel", cjump.getTrueLabel());
				instruction.addAttribute("falseLabel", cjump.getFalseLabel());
				instruction.addAttribute("relop", getRelationalString(cjump.getRelOp()));
				if(cjump.getImmediateValue()!=null)
				instruction.addAttribute("im", cjump.getImmediateValue()+"");				
			}
			if(ins.getType()==AssemblyInstruction.JUMP){
				Jump jump = (Jump)ins;
				//System.out.println("creating jump instruction"+jump.getLabel());
				instruction.addAttribute("label", jump.getLabel());
			}
			if(ins.getType()==AssemblyInstruction.LABEL){
				LabelInstr label = (LabelInstr)ins;
				instruction.addAttribute("label", label.getLabelStr());
			}
			if(ins.getType()== AssemblyInstruction.INSTRUCTION){
				Operation operation = (Operation)ins;
				operation.getOperationCode();
				if(operation.getImmediateValue()!=null)
					instruction.addAttribute("im", operation.getImmediateValue()+"");
				
			}
			if(srcList != null)
				for(Object variable:srcList){
					instruction.addElement("use").addText(variable.toString());
				}
			for(Object defenition:defenitions){			
				instruction.addElement("def").addText(defenition.toString());
			}

		}
		
		if(nodeInfoBean.getLeftChild()!=null){
			xmlNode.addElement("left_child").addText(nodeInfoBean.getLeftChild()+"");
		}
		if(nodeInfoBean.getRightChild()!=null){
			xmlNode.addElement("right_child").addText(nodeInfoBean.getRightChild()+"");
		}

	}

	public String getInstructionType(int insType,AssemblyInstruction instruction){
		if(insType==AssemblyInstruction.CALL){
			return "call";		
		}
		else if(insType==AssemblyInstruction.CJUMP){
			return "cjump";		
		}
		else if(insType==AssemblyInstruction.LABEL){
			return "label";		
		}
		else if(insType==AssemblyInstruction.INSTRUCTION){
			return getStringForOperation(instruction);		
		}
		else if(insType==AssemblyInstruction.SSA_PHI_FUNCTION){
			return "phi_func";		
		}
		else if(insType==AssemblyInstruction.JUMP){
			return "jump";		
		}
		else {
			return "none";
		}
	}

	public XMLStringBuffer getTextForGeneratedXml(){

		String buffer = new String();
		XMLStringBuffer stringBuffer = new XMLStringBuffer(buffer);	

		try{
			SAXReader reader1 = new SAXReader();
			FileReader fileReader = new FileReader(new File("xmlFromCFG.xml"));
			org.dom4j.Document document = reader1.read(fileReader);

			FileReader reader = new FileReader(new File("xmlFromCFG.xml"));
			BufferedReader bufferedReader = new BufferedReader(reader);
			while(bufferedReader.ready()){
				stringBuffer.append(bufferedReader.readLine());
			}		
			System.out.println(stringBuffer.toString());

			return stringBuffer;
		}
		catch(Exception ex){
			ex.printStackTrace();
		}
		return null;
	}

	public void generateDataForXml(ControlFlowGraph cfg){

		Vector successors = new Vector();
		for(int i = 0;i<cfg.getNumNodes(); i++){
			NodeInfoBean nodeInfoBean = new NodeInfoBean();
			nodeInfoBean.setId(i+1);
			nodeInfoBean.setNode(cfg.getNode(i));
			nodesInfo.add(nodeInfoBean);
		}	

		for(NodeInfoBean nodeInfo:nodesInfo){
			Vector<NodeInfoBean> childs = getChildNodesBean(nodeInfo);
			if(childs.size()>0){
				if(childs.size()==1){
					nodeInfo.setLeftChild(childs.get(0).getId());
				}
				if(childs.size()>1){
					nodeInfo.setLeftChild(childs.get(0).getId());
					nodeInfo.setRightChild(childs.get(1).getId());
				}
			}
			nodeInfo.setSuccessors(childs);
		}

	}


	public Vector getChildNodesBean(NodeInfoBean nodeInfoBean){
		Node node = nodeInfoBean.getNode();
		Vector childs = new Vector();


		for(NodeInfoBean nodeInfo:nodesInfo){

			for(Node childNode:((Vector<Node>)node.getSuccessors())){
				if(childNode.getName().equals(nodeInfo.getNode().getName())){
					childs.add(nodeInfo);					
				}
			}

		}

		return childs;
	}

	public String getRelationalString(int value){
		switch (value) {
			case 0:return "EQ";
			case 1:return "NE";
			case 2:return "LT";
			case 3:return "GT";
			case 4:return "LE";
			case 5:return "GE";
			case 6:return "ULT";
			case 7:return "ULE";
			case 8:return "UGT";
			case 9:return "UGE";
			default:return "";
		}
	}
	
	public String getStringForOperation(AssemblyInstruction instruction){
		
		Operation operation = (Operation)instruction;
		
		switch (operation.getOperationCode()){
		case 0: return "ADD";
		case 1: return "MINUS";
		case 2: return "MUL";
		case 3: return "DIV";
		case 4: return "LOAD_IMMEDIATE_VALUE";
		case 5: return "STORE_WORD_INTO_ADDRESS_AT_TEMP";
		case 6: return "STORE_WORD_INTO_CONST_ADDRESS";
		case 7: return "MOVE_TEMP_TO_TEMP";
		case 8: return "LOAD_ADDRESS";
		case 9: return "LOAD_FROM_TEMP_OFFSET_EXP_MEM";
			default:
				return "";				
		}		
	}

}
